package aor2.cpu.blockinterrupt;

import java.util.Scanner;

import aor2.components.ElementIO;
import aor2.components.ElementIOException;
import aor2.components.Restorable;
import aor2.components.Signal;
import aor2.cpu.InternBus;
import aor2.cpu.blockcontrol.ConditionSignalSet;
import aor2.cpu.blockcontrol.ControlSignalSet;
import aor2.cpu.blockoperation.Psw;

public class InterruptBlock implements ElementIO, Restorable {
	private static InterruptBlock singleton = null;

	public static InterruptBlock getInstance() {
		if (singleton == null)
			singleton = new InterruptBlock();
		return singleton;
	}

	private Imr imr;
	private ExternInterruptBoard extInterr;
	private InternInterruptBoard intInterr;
	private Ivtp ivtp;
	private Psw psw;
	private Signal primm, notprekid, prinm, prins, prcod, pradr;
	boolean irm1, irm2, irm3, imrprirr, accprirr, prL0, prL1;
	int prIntrr;

	private InterruptBlock() {
		imr = new Imr();
		extInterr = new ExternInterruptBoard();
		intInterr = new InternInterruptBoard();
		// FIXME ivtp state
		ivtp = new Ivtp(0x1000L);
		psw = Psw.getInstance();
		irm1 = irm2 = irm3 = imrprirr = accprirr = prL0 = prL1 = false;
		ConditionSignalSet condss = ConditionSignalSet.getInstance();
		primm = condss.get("PRIMM");
		prinm = condss.get("PRINM");
		pradr = condss.get("PRADR");
		prcod = condss.get("PRCOD");
		prins = condss.get("PRINS");
		prIntrr = 0;
		notprekid = ConditionSignalSet.getInstance().get("notprekid");
	}

	public void next() {
		// XXX proveriti
		intInterr.next();
		extInterr.next();
		irm1 = extInterr.qprirr1.isActive() && imr.imr1();
		irm2 = extInterr.qprirr2.isActive() && imr.imr2();
		irm3 = extInterr.qprirr3.isActive() && imr.imr3();

		imrprirr = irm1 || irm2 || irm3;

		if (irm3) {
			prL0 = prL1 = true;
			prIntrr = 3;
		} else if (irm2) {
			prL0 = false;
			prL1 = true;
			prIntrr = 2;
		} else if (irm1) {
			prL0 = true;
			prL1 = false;
			prIntrr = 1;
		} else {
			prL0 = prL1 = false;
			prIntrr = 0;
		}

		boolean L0, L1;
		int currentPrirr = 0;
		L0 = psw.bitL0();
		L1 = psw.bitL1();
		if (L1)
			currentPrirr += 2;
		if (L0)
			currentPrirr += 1;

		accprirr = prIntrr > currentPrirr;

		primm.setActive(accprirr && imrprirr && psw.bitI());
		notprekid
				.setActive(!(primm.isActive() || prins.isActive()
						|| prcod.isActive() || pradr.isActive()
						|| prinm.isActive() || psw.bitT()));
		// System.out.println("\t>>"+notprekid +
		// " :"+primm+prins+prcod+pradr+prinm+psw.bitT());
	}

	public boolean newL1() {
		return prL1;
	}

	public boolean newL0() {
		return prL0;
	}

	public Imr getImr() {
		return imr;
	}

	public ExternInterruptBoard getExtInterr() {
		return extInterr;
	}

	public InternInterruptBoard getIntInterr() {
		return intInterr;
	}

	public Ivtp getIvtp() {
		return ivtp;
	}

	public boolean isIrm1() {
		return irm1;
	}

	public boolean isIrm2() {
		return irm2;
	}

	public boolean isIrm3() {
		return irm3;
	}

	public boolean isImrprirr() {
		return imrprirr;
	}

	public boolean isAccprirr() {
		return accprirr;
	}

	public boolean isPrL0() {
		return prL0;
	}

	public boolean isPrL1() {
		return prL1;
	}

	public int getPrIntrr() {
		return prIntrr;
	}

	@Override
	public void in() throws ElementIOException {
		// ignore
	}

	@Override
	public void out() throws ElementIOException {
		if (ControlSignalSet.getInstance().get("BRUNMSK").isActive()) {
			InternBus.M2.setState(nmskout() & 0xFFFF);
		} else if (ControlSignalSet.getInstance().get("BRUMSK").isActive()) {
			InternBus.M2.setState(mskout() & 0xFFFF);
		}
	}

	public long nmskout() {
		long entry = 0;
		if (prcod.isActive())
			entry += 3;
		else if (pradr.isActive())
			entry += 2;
		else if (prinm.isActive())
			entry += 1;
		else if (psw.bitT())
			entry += 0;
		return entry;
	}

	public long mskout() {
		long entry = 4;
		if (irm3)
			entry += 3;
		else if (irm2)
			entry += 2;
		else if (irm1)
			entry += 1;
		else
			entry = 0;
		return entry;
	}

	@Override
	public void restore(String restorePoint) {

		String delimiter = "<interruptblockdelim>";

		String trim = restorePoint.replace(beginTag(), "");
		trim = trim.replace(endTag(), "");
		trim = trim.trim();

		Scanner s = new Scanner(trim);
		s.useDelimiter(delimiter);

		imr.restore(s.next());
		extInterr.restore(s.next());
		intInterr.restore(s.next());
		ivtp.restore(s.next());
		prIntrr = Integer.parseInt(s.next());

		irm1 = extInterr.qprirr1.isActive() && imr.imr1();
		irm2 = extInterr.qprirr2.isActive() && imr.imr2();
		irm3 = extInterr.qprirr3.isActive() && imr.imr3();

		imrprirr = irm1 || irm2 || irm3;

		if (irm3) {
			prL0 = prL1 = true;
			prIntrr = 3;
		} else if (irm2) {
			prL0 = false;
			prL1 = true;
			prIntrr = 2;
		} else if (irm1) {
			prL0 = true;
			prL1 = false;
			prIntrr = 1;
		} else {
			prL0 = prL1 = false;
			prIntrr = 0;
		}

		boolean L0, L1;
		int currentPrirr = 0;
		L0 = psw.bitL0();
		L1 = psw.bitL1();
		if (L1)
			currentPrirr += 2;
		if (L0)
			currentPrirr += 1;

		accprirr = prIntrr > currentPrirr;

		s.close();
	}

	@Override
	public String createRestorePoint() {
		String delimiter = "<interruptblockdelim>";
		return beginTag() + "\n\t" + imr.createRestorePoint() + delimiter
				+ extInterr.createRestorePoint() + delimiter
				+ intInterr.createRestorePoint() + delimiter
				+ ivtp.createRestorePoint() + delimiter + prIntrr + "\n"
				+ endTag();
	}

	@Override
	public String beginTag() {
		return "<InterruptBlock>";
	}

	@Override
	public String endTag() {
		return "</InterruptBlock>";
	}

	public static void main(String[] args) {
		String restorePoint = InterruptBlock.getInstance().createRestorePoint();
		System.out.println(restorePoint);

		InterruptBlock.getInstance().restore(restorePoint);
	}

	public void previousClk() {
		imr.previousClk();
		extInterr.previousClk();
		intInterr.previousClk();
		// FIXME ivtp state
		ivtp.previousClk();
		irm1 = extInterr.qprirr1.isActive() && imr.imr1();
		irm2 = extInterr.qprirr2.isActive() && imr.imr2();
		irm3 = extInterr.qprirr3.isActive() && imr.imr3();
		imrprirr = irm1 || irm2 || irm3;

		if (irm3) {
			prL0 = prL1 = true;
			prIntrr = 3;
		} else if (irm2) {
			prL0 = false;
			prL1 = true;
			prIntrr = 2;
		} else if (irm1) {
			prL0 = true;
			prL1 = false;
			prIntrr = 1;
		} else {
			prL0 = prL1 = false;
			prIntrr = 0;
		}

		int currentPrirr = 0;
		boolean L0 = psw.bitL0(), L1 = psw.bitL1();
		if (L1)
			currentPrirr += 2;
		if (L0)
			currentPrirr += 1;
		accprirr = prIntrr > currentPrirr;
		primm.setActive(accprirr && imrprirr && psw.bitI());
		notprekid
				.setActive(!(primm.isActive() || prins.isActive()
						|| prcod.isActive() || pradr.isActive()
						|| prinm.isActive() || psw.bitT()));
	}

}
